package com.cnap.service.impl;

import com.cnap.db.entity.IndicatorCasePO;
import com.cnap.db.entity.IndicatorPO;
import com.cnap.db.entity.MicroServicePO;
import com.cnap.db.entity.ScheduleStrategyPO;
import com.cnap.db.mapper.ApplicationMapper;
import com.cnap.db.mapper.IndicatorMapper;
import com.cnap.db.mapper.MicroServiceMapper;
import com.cnap.db.mapper.ScheduleStrategyMapper;
import com.cnap.model.common.PageResult;
import com.cnap.model.exception.JsonException;
import com.cnap.model.indicator.*;
import com.cnap.model.strategy.SvcConstants;
import com.cnap.model.strategy.schedule.BusinessScene;
import com.cnap.model.strategy.schedule.SvcTypeEnum;
import com.cnap.service.IndicatorService;
import com.cnap.utils.PageUtils;
import com.github.pagehelper.PageInfo;
import com.github.pagehelper.page.PageMethod;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <类文件描述>
 *
 * @author xc
 **/
@Service
public class IndicatorServiceImpl implements IndicatorService {

    private static final Logger LOGGER = LoggerFactory.getLogger(IndicatorServiceImpl.class);

    private final IndicatorMapper indicatorMapper;
    private final MicroServiceMapper microServiceMapper;
    private final ApplicationMapper applicationMapper;
    private final ScheduleStrategyMapper scheduleStrategyMapper;

    @Autowired
    public IndicatorServiceImpl(IndicatorMapper indicatorMapper, MicroServiceMapper microServiceMapper,
                                ApplicationMapper applicationMapper, ScheduleStrategyMapper scheduleStrategyMapper) {
        this.indicatorMapper = indicatorMapper;
        this.microServiceMapper = microServiceMapper;
        this.applicationMapper = applicationMapper;
        this.scheduleStrategyMapper = scheduleStrategyMapper;
    }

    /**
     * 批量插入指标实例
     *
     * @param indicatorCasePOS 列表
     * @return 结果
     */
    @Override
    public boolean batchInsertIndicatorCases(List<IndicatorCasePO> indicatorCasePOS) {
        // 批量插入，可以优化，进行分批插入，防止单次插入过大，超过数据库单次长度
        int s = indicatorMapper.batchInsertIndicatorCases(indicatorCasePOS);
        return s == indicatorCasePOS.size();
    }

    /**
     * 根据指标类型查询
     *
     * @param indicatorType 指标类型，算力cal，网络net
     * @param category      分类，AI 大数据
     * @param type          二级分类，AI：静态训练，动态训练，大数据：离线处理，实时处理
     * @return 结果
     */
    @Override
    public List<IndicatorPO> getIndicatorByIndicatorTypeAndCategoryAndType(String indicatorType, String category, String type) {
        // 根据指标类型、分类和二级分类查询，字段由调用方保证正确
        return indicatorMapper.selectByIndicatorTypeAndCategoryAndType(indicatorType, category, type);
    }

    /**
     * 根据指标类型查询所有
     *
     * @param category 分类，AI 大数据
     * @param type     二级分类，AI：静态训练，动态训练，大数据：离线处理，实时处理
     * @return 结果
     */
    @Override
    public List<IndicatorPO> getIndicatorByCategoryAndType(String category, String type) {
        return indicatorMapper.selectByCategoryAndType(category, type);
    }

    /**
     * 根据服务id查询所有指标
     *
     * @param serviceId 服务id
     * @return 结果
     */
    @Override
    public IndicatorVO getAllIndicatorByServiceId(String serviceId) {
        MicroServicePO microServicePO = microServiceMapper.selectMicroServiceById(serviceId);
        String appId = microServicePO.getApplicationId();
        // 算力指标
        List<IndicatorCasePO> calIndicatorCasePOs = indicatorMapper.selectByApplicationIdAndIndicatorType(appId,
                IndicatorTypeEnum.CAL.getValue());
        // 网络指标
        List<IndicatorCasePO> netIndicatorCasePOs = indicatorMapper.selectByApplicationIdAndIndicatorType(appId,
                IndicatorTypeEnum.NET.getValue());
        // 算力指标
        List<IndicatorInfo> calInfos = calIndicatorCasePOs.stream().map(IndicatorInfo::toInfo).collect(Collectors.toList());
        // 网络指标
        List<IndicatorInfo> netInfos = netIndicatorCasePOs.stream().map(IndicatorInfo::toInfo).collect(Collectors.toList());

        // 组装vo
        IndicatorVO indicatorVO = new IndicatorVO();
        // id
        indicatorVO.setServiceId(serviceId);
        // 应用id
        indicatorVO.setApplicationId(appId);
        // 网络指标
        indicatorVO.setNetIndicators(netInfos);
        // 计算指标
        indicatorVO.setCalIndicators(calInfos);

        return indicatorVO;
    }

    /**
     * 根据应用id删除指标实例
     *
     * @param appId 应用id
     * @return 结果
     */
    @Override
    public boolean deleteIndicatorCaseByAppId(String appId) {
        return indicatorMapper.deleteByApplicationId(appId) > 0;
    }

    @Override
    public boolean deleteIndicatorCaseBySvcId(String svcId) {
        return indicatorMapper.deleteBySvcId(svcId) > 0;
    }

    /**
     * 分页查询
     *
     * @param queryIndicatorParam 参数
     * @return 结果
     */
    @Override
    public PageResult<IndicatorPO> findByPage(QueryIndicatorParam queryIndicatorParam) {
        PageMethod.startPage(queryIndicatorParam.getPageNum(), queryIndicatorParam.getPageSize());
        List<IndicatorPO> indicatorPOS = indicatorMapper.selectIndicatorByParam(queryIndicatorParam);
        PageInfo<IndicatorPO> pageInfo = new PageInfo<>(indicatorPOS);
        return PageUtils.getPageResult(pageInfo);
    }

    /**
     * 批量更新值
     *
     * @param params 参数
     */
    @Transactional
    @Override
    public void batchUpdateValues(List<UpdateIndicatorParam> params) {
        for (UpdateIndicatorParam param : params) {
            // 只需要更新case表中的数据
            indicatorMapper.updateIndicatorcaseValueById(param.getId(), param.getValue());
        }
    }

    @Override
    public List<String> queryScenes(QuerySceneParam querySceneParam) {
        return microServiceMapper.selectLikeName(querySceneParam);
    }

    @Override
    public List<String> queryIndicatorNames(QueryIndicatorNameParam queryIndicatorNameParam) {
        return indicatorMapper.selectLikeName(queryIndicatorNameParam);
    }

    @Override
    public TreeNode findIndicatorCase(QueryIndicatorCaseParam queryIndicatorCaseParam) {
        List<IndicatorCaseInfo> casePOS = indicatorMapper.selectInfoByParam(queryIndicatorCaseParam);

        // scene(微服务名称)可能重复，此处使用svcId_scene作为key
        Map<String, List<IndicatorCaseInfo>> sceneMap = casePOS.stream()
                .collect(Collectors.groupingBy(iCase -> iCase.getSvcId() + "_" + iCase.getScene()));

        Map<String, List<TreeNode>> categoryMap = new LinkedHashMap<>();

        for (Map.Entry<String, List<IndicatorCaseInfo>> entry : sceneMap.entrySet()) {
            // key为svcId_scene
            String scene = entry.getKey().split("_")[1];
            Map<String, List<IndicatorCaseInfo>> indicatorMap = entry.getValue().stream()
                    .collect(Collectors.groupingBy(IndicatorCaseInfo::getIndicatorType, LinkedHashMap::new, Collectors.toList()));

            String category = entry.getValue().get(0).getCategory();
            String svcId = entry.getValue().get(0).getSvcId();

            List<TreeNode> categoryNodes;
            TreeNode microNode = new TreeNode();
            microNode.setMsg(scene);
            microNode.setId(svcId);

            // 判断当前微服务是否为cpu或gpu
            String computeArch = getComputeArchBySvcId(svcId);
            microNode.setComputeArch(computeArch);

            List<TreeNode> microChildren = new ArrayList<>();

            if (indicatorMap.containsKey(IndicatorTypeEnum.CAL.getValue())) {
                TreeNode calNode = new TreeNode();
                calNode.setMsg(IndicatorTypeEnum.CAL.getValue());
                calNode.setChildren(getTreeNodeFromIndicators(indicatorMap.get(IndicatorTypeEnum.CAL.getValue())));
                microChildren.add(calNode);
            }
            if (indicatorMap.containsKey(IndicatorTypeEnum.NET.getValue())) {
                TreeNode netNode = new TreeNode();
                netNode.setMsg(IndicatorTypeEnum.NET.getValue());
                netNode.setChildren(getTreeNodeFromIndicators(indicatorMap.get(IndicatorTypeEnum.NET.getValue())));
                microChildren.add(netNode);
            }
            microNode.setChildren(microChildren);

            if (categoryMap.containsKey(category)) {
                categoryNodes = categoryMap.get(category);
            } else {
                categoryNodes = new ArrayList<>();
                categoryMap.put(category, categoryNodes);
            }
            categoryNodes.add(microNode);

        }

        TreeNode rootNode = new TreeNode();
        rootNode.setMsg("应用");
        List<TreeNode> rootChildren = new LinkedList<>();
        for (Map.Entry<String, List<TreeNode>> entry : categoryMap.entrySet()) {
            TreeNode categoryNode = new TreeNode();
            categoryNode.setMsg(entry.getKey());
            categoryNode.setChildren(entry.getValue());
            rootChildren.add(categoryNode);
        }
        rootNode.setChildren(rootChildren);

        return rootNode;
    }

    private List<TreeNode> getTreeNodeFromIndicators(List<IndicatorCaseInfo> infoList) {
        return infoList.stream().map(info -> {
            TreeNode treeNode = new TreeNode();
            treeNode.setMsg(info.getIndicatorName());
            treeNode.setId(info.getCaseId());
            String leafNodeMsg = info.getValue() + "(" + info.getUnit() + ")";
            TreeNode leafNode = new TreeNode();
            leafNode.setMsg(leafNodeMsg);
            treeNode.setChildren(Collections.singletonList(leafNode));
            return treeNode;
        }).collect(Collectors.toList());
    }

    /**
     * 根据微服务id查询cou或gpu
     *
     * @param svcId svcId
     * @return 结果
     */
    private String getComputeArchBySvcId(String svcId) {
        ScheduleStrategyPO scheduleStrategyPO = scheduleStrategyMapper.selectByServiceId(svcId);
        if (Objects.isNull(scheduleStrategyPO)) {
            return SvcConstants.CPU;
        }
        BusinessScene businessScene = scheduleStrategyPO.getBusinessScene();
        if (Objects.nonNull(businessScene) && SvcTypeEnum.AI_TRAIN.getSvcType().equals(businessScene.getSvcType())) {
            return SvcConstants.GPU;
        }
        return SvcConstants.CPU;
    }

    @Override
    public boolean updateCaseValue(UpdateCaseParam updateCaseParam) {
        MicroServicePO svcPO = microServiceMapper.selectMicroServiceById(updateCaseParam.getSvcId());
        if (Objects.isNull(svcPO)) {
            LOGGER.error("[indicator]: update case error, service is not exist, svcId={}", updateCaseParam.getSvcId());
            throw new JsonException(404, "该微服务不存在");
        }
        int result = indicatorMapper.updateCaseByParam(updateCaseParam);
        return result == 1;
    }
}
